
#include "Log.h"
#include <stdarg.h>


void LogEvent::format(const char* fmt, ...) {
    va_list al;
    va_start(al, fmt);
    format(fmt, al);
    va_end(al);
}

void LogEvent::format(const char* fmt, va_list al) {
    char* buf = nullptr;
    int len = vasprintf(&buf, fmt, al);
    if(len != -1) {
        m_ss << std::string(buf, len);
        free(buf);
    }
}

Logger::Logger(const std::string &name)
: m_name(name)
, m_level(LogLevel::INFO)
, m_createTime(GetElapsedMS()) {

}

void Logger::addAppender(LogAppender::ptr appender) {
    m_appenders.push_back(appender);
}

void Logger::delAppender(LogAppender::ptr appender) {
    for (auto it = m_appenders.begin(); it != m_appenders.end(); it++) {
        if (*it ==appender) {
            m_appenders.erase(it);
            return;
        }
    }
}

//学习macro使用
const char* LogLevel::ToString(LogLevel::Level level) {
    switch(level) {
#define XX(name) \
    case LogLevel::name: \
        return #name; \
        break;

        XX(DEBUG);
        XX(INFO);
        XX(WARN);
        XX(ERROR);
        XX(FATAL);
#undef XX
        default:
            return "UNKNOW";
    }
}

void Logger::log(LogLevel::Level level, LogEvent::ptr event) {
    if (level >= m_level) {
        auto self = shared_from_this();
        for (auto& it: m_appenders) {
            it->log(self, level, event);
        }
    }
}

void Logger::debug(LogEvent::ptr event) {
    log(LogLevel::DEBUG, event);
}
void Logger::info(LogEvent::ptr event) {
    log(LogLevel::INFO, event);
}
void Logger::warn(LogEvent::ptr event) {
    log(LogLevel::WARN, event);
}
void Logger::error(LogEvent::ptr event) {
    log(LogLevel::ERROR, event);
}
void Logger::fatal(LogEvent::ptr event) {
    log(LogLevel::FATAL, event);
}

FileLogAppender::FileLogAppender(const std::string &filename)
        :m_filename(filename)
{}

void FileLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
    if (level >= m_level) {
        reopen();
        m_filestream << m_formatter->format(logger, level, event);
    }
}

bool FileLogAppender::reopen() {
    if(m_filestream)
        m_filestream.close();
    m_filestream.open(m_filename, std::ios::out|std::ios::app);
    return !!m_filestream;
}


void StdoutLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
    if(level >= m_level) {
        m_formatter->format(std::cout, logger, level, event);
    }
}

LogFormatter::LogFormatter(const std::string& pattern)
        :m_pattern(pattern) {
    init();
}

std::string LogFormatter::format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
    std::stringstream ss;
    for(auto& i : m_items) {
        i->format(ss, logger, level, event);
    }
    return ss.str();
}

std::ostream& LogFormatter::format(std::ostream& ofs, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
    for(auto& i : m_items) {
        i->format(ofs, logger, level, event);
    }
    return ofs;
}

class StringFormatItem : public LogFormatter::FormatItem {
public:
    StringFormatItem(const std::string& str)
            :m_string(str) {}
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << m_string;
    }
private:
    std::string m_string;
};

class NewLineFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << std::endl;
    }
};

class MessageFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getContent();
    }
};

class LevelFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << LogLevel::ToString(event->getLevel());
    }
};

class LoggerNameFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getLogger()->getName();
    }
};


class DateTimeFormatItem : public LogFormatter::FormatItem {
public:
    DateTimeFormatItem(const std::string& str = "%Y-%m-%d %H:%M:%S")
            :m_string(str)
    {
        if(m_string.empty()) {
            m_string = "%Y-%m-%d %H:%M:%S";
        }
    }
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        struct tm tm;
        time_t time = event->getTime();
        localtime_r(&time, &tm);
        char buf[64];
        strftime(buf, sizeof(buf), m_string.c_str(), &tm);
        os << buf;
    }
private:
    std::string m_string;
};

class ElapseFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getElapse();
    }
};

class FileNameFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getFile();
    }
};

class LineFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getLine();
    }
};

class ThreadIdFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getThreadId();
    }
};

class ThreadNameFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getThreadName();
    }
};

class TabFormatItem : public LogFormatter::FormatItem {
public:
    void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << "\t";
    }
};

/*
 * 模版字符串："%d{%Y-%m-%d %H:%M:%S} [%rms]%T%t%T%N%T[%p]%T[%c]%T%f:%l%T%m%n"
 *  %m 消息
    %p 日志级别
    %c 日志器名称
    %d 日期时间，后面可跟一对括号指定时间格式，比如%d{%Y-%m-%d %H:%M:%S}，这里的格式字符与C语言strftime一致
    %r 该日志器创建后的累计运行毫秒数
    %f 文件名
    %l 行号
    %t 线程id
    %N 线程名称
    %% 百分号
    %T 制表符
    %n 换行
 */
void LogFormatter::init() {
    for(size_t i = 0; i < m_pattern.size(); ++i) {
        if(m_pattern[i] == '%') {
            if(i + 1 < m_pattern.size()) {
                switch (m_pattern[i+1]) {
                    case 'm':
                        m_items.push_back((FormatItem::ptr)(new MessageFormatItem()));
                        i++;
                        break;
                    case 'p':
                        m_items.push_back((FormatItem::ptr)(new LevelFormatItem()));
                        i++;
                        break;
                    case 'c':
                        m_items.push_back((FormatItem::ptr)(new LoggerNameFormatItem()));
                        i++;
                        break;
                    case 'd':
                        if(i + 2 < m_pattern.size()) {
                            if(m_pattern[i+2] == '{') {
                                size_t end = i + 3;
                                while(end < m_pattern.size() && m_pattern[end] != '}') {
                                    end++;
                                }
                                if(end == m_pattern.size()) {
                                    m_error = true;
                                    return;
                                }
                                m_items.push_back((FormatItem::ptr)(new DateTimeFormatItem(m_pattern.substr(i+3, end-(i+3)))));
                                i = end;
                                break;
                            } else {
                                m_error = true;
                                return;
                            }
                        } else {
                            m_error = true;
                            return;
                        }
                    case 'r':
                        m_items.push_back((FormatItem::ptr)(new ElapseFormatItem()));
                        i++;
                        break;
                    case 'f':
                        m_items.push_back((FormatItem::ptr)(new FileNameFormatItem()));
                        i++;
                        break;
                    case 'l':
                        m_items.push_back((FormatItem::ptr)(new LineFormatItem()));
                        i++;
                        break;
                    case 't':
                        m_items.push_back((FormatItem::ptr)(new ThreadIdFormatItem()));
                        i++;
                        break;
                    case 'N':
                        m_items.push_back((FormatItem::ptr)(new ThreadNameFormatItem()));
                        i++;
                        break;
                    case 'T':
                        m_items.push_back((FormatItem::ptr)(new TabFormatItem()));
                        i++;
                        break;
                    case 'n':
                        m_items.push_back((FormatItem::ptr)(new NewLineFormatItem()));
                        i++;
                        break;
                }
            }
        } else {
            size_t begin = i;
            while(i < m_pattern.size() && m_pattern[i] != '%') {
                i++;
            }
            m_items.push_back((FormatItem::ptr) (new StringFormatItem(m_pattern.substr(begin, i -begin))));
            i--;
        }
    }
}


LoggerManager::LoggerManager() {
    m_root.reset(new Logger("root"));
    LogFormatter::ptr fmt(new LogFormatter("%d{} %T%r%T%t%T%N%T[%p]%T[%c]%T%f:%l%T%m%n"));
    m_root->addAppender(LogAppender::ptr(new StdoutLogAppender(LogLevel::UNKNOWN, fmt)));

    m_loggers[m_root->m_name] = m_root;

    Logger::ptr system_logger(new Logger("system"));
    system_logger->m_root = m_root;
    system_logger->addAppender(LogAppender::ptr(new FileLogAppender("../log/system.txt", LogLevel::UNKNOWN, fmt)));
    m_loggers["system"] = system_logger;

    init();
}

Logger::ptr LoggerManager::getLogger(const std::string& name) {
    auto it = m_loggers.find(name);
    if(it != m_loggers.end()) {
        return it->second;
    }

    Logger::ptr logger(new Logger(name));
    logger->m_root = m_root;
    m_loggers[name] = logger;
    return logger;
}

void LoggerManager::init() {
}

